package com.xiao.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiao.domain.ResponseResult;
import com.xiao.domain.constants.SystemConstants;
import com.xiao.domain.dto.AddMenuDto;
import com.xiao.domain.dto.MenuListDto;
import com.xiao.domain.vo.MenuListVO;
import com.xiao.domain.vo.MenuTreeSelectVo;
import com.xiao.domain.vo.RoleMenuTreeByIdVo;
import com.xiao.enums.AppHttpCodeEnum;
import com.xiao.mapper.SysMenuMapper;
import com.xiao.domain.pojo.SysMenu;
import com.xiao.service.SysMenuService;
import com.xiao.utils.BeanCopyUtils;
import com.xiao.utils.SecurityUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.stream.Collectors;


// 菜单权限 业务接口实现
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Override
    public List<String> selectPermsByUserId(Long userId) {

        if (userId==1L){// 如果是管理员 返回所有权限
            //1. 构造条件
            LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(SysMenu::getMenuType,SystemConstants.MENU_TYPE_BUTTON,SystemConstants.MENU_TYPE_MENU); // 按钮和菜单
            wrapper.eq(SysMenu::getStatus,SystemConstants.STATUS_NORMAL) ;// 状态正常
            // 2. 查询
            List<SysMenu> menus = this.list(wrapper);
            // 3. 获取 perms 以 list 返回
            List<String> perms = menus.stream().map(SysMenu::getPerms)
                    .collect(Collectors.toList());
            return perms;
        } else { // 否则返回，ID所具有的所有权限
            return getBaseMapper().selectPermsByUserId(userId);

        }

    }

    @Override
    public List<SysMenu> selectRouterMenuTreeByUserId(Long userId) {
        SysMenuMapper baseMapper = this.getBaseMapper();
        List<SysMenu> menus = null;
        // 1, 判断，登录用户是否是管理员
        if (SecurityUtils.isAdmin()){ // 返回所有 menu
            menus=baseMapper.selectAllRouterMenu();
        } else { // 查询用户对应所有菜单列表 （这个时候，还不是树,我们需要根据这个列表做成树，展示）
            menus=baseMapper.selectRouterMenuTreeByUserId(userId); // 这里还是 列表的形式 不是 tree
        }
        // 2, 把list 构建成 tree
        // 先找出第一层菜单,然后去找他们的子菜单，设置到 children 中
        List<SysMenu> menuTree = this.buildMenuTree(menus,SystemConstants.FIRST_MENU_ID);// 0L 顶级菜单ID，估计每个人都至少有这个

        return menuTree;
    }

    @Override
    public ResponseResult<MenuListVO> menuAllList(MenuListDto menuListDto) {
        // 1,构建查询规则
        // 需要映射到我们原本的 pojo 才能使用
        SysMenu sysMenu = BeanCopyUtils.copyBean(menuListDto, SysMenu.class);

        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();

        wrapper.like(StringUtils.hasText(sysMenu.getMenuName()),SysMenu::getMenuName,sysMenu.getMenuName());
        wrapper.like(StringUtils.hasText(sysMenu.getStatus()),SysMenu::getStatus,sysMenu.getStatus());


        wrapper.orderByAsc(SysMenu::getParentId);
        wrapper.orderByAsc(SysMenu::getOrderNum);


        // 2,查询
        List<SysMenu> list = this.list(wrapper);
        // 需要封装成 VO
        List<MenuListVO> menuListVOS = BeanCopyUtils.copyBeanList(list, MenuListVO.class);

        //3,统一响应
        ResponseResult<MenuListVO> responseResult = ResponseResult.okResult(menuListVOS);

        return responseResult;
    }

    @Override
    public ResponseResult addMenu(AddMenuDto addMenuDto) {

        // 1. 转化成 pojo
        SysMenu sysMenu = BeanCopyUtils.copyBean(addMenuDto, SysMenu.class);
        // 2. 插入数据
        boolean isSave = this.save(sysMenu);
        // 3. 统一响应
        ResponseResult responseResult;

        if (isSave){
            responseResult=ResponseResult.okResult();
        }else {
            responseResult=ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
        }
        return responseResult;
    }

    @Override
    public ResponseResult delMenuById(Integer menuId) {
        // 统一响应
        ResponseResult responseResult=null;
        // 1. 判断是否有子菜单
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysMenu::getParentId,menuId);
        List<SysMenu> list = this.list(wrapper);
        if (list.isEmpty()){

            // 1.1 没有
            int i = this.getBaseMapper().deleteById(menuId);
            if (i>0){
                responseResult=ResponseResult.okResult();
            }
        }else {
            // 1.2 有
            responseResult=ResponseResult.errorResult(500,"存在子菜单不允许删除");
        }

        return responseResult;
    }

    @Override
    public ResponseResult<MenuListVO> selectMenuById(Integer menuId) {
        // 1. 查询到pojo
        SysMenu sysMenu = this.getById(menuId);
        // 2. 拷贝到vo
        MenuListVO menuListVO = BeanCopyUtils.copyBean(sysMenu, MenuListVO.class);

        // 3. 统一响应
        ResponseResult responseResult = ResponseResult.okResult(menuListVO);

        return responseResult;
    }
    private List<SysMenu> buildMenuTree(List<SysMenu> menus,Long parentId){
        List<SysMenu> menuTree = menus.stream()
                .filter(menu -> menu.getParentId().equals(parentId))// 过滤 只要符合要求的 menu ，也就是 parentId = 0 ,结果可能是多个.最外面的顶级层级
                // 开始设置 过滤后得到的(parentId = 0)的 menu children 集合,我们根据菜单 ID 去查，如果 parent_id = 该id 说明就是他的子菜单也就是他的 children
                .map(menu -> menu.setChildren(this.getChildren(menu.getId(), menus)))
                .collect(Collectors.toList()); // 收集成 列表; 这里指展示 parentId = 0 的 ,其他都是 以 children 树的形式展示。
        return menuTree;
    }
    /**
     例如： 有一用户ID 2 ，
     通过查询    selectRouterMenuTreeByUserId(userId) 他的角色ID 对应 2,菜单有: 菜单管理 和 系统管理

     */

    // 根据 菜单ID ，和菜单列表，查询子菜单集合。(原本只查一层，但是我们递归调用自己，所以会查不到为止)
    // 这种递归写法，最简单的思路就是，先想到怎么查一层，之后递归调用自己就可以
    private List<SysMenu>  getChildren(Long id,List<SysMenu> menus){
        List<SysMenu> childrenList = menus.stream()
                .filter(m -> m.getParentId().equals(id)) // 保留 menus 列表中 parent_id = 传递来的 菜单ID,到这里，如果直接返回的话，完成了 下一级的查询。
                // set 当前查到的子菜单的子菜单，所以是用当前子菜单的id 去查
                .map(m->m.setChildren(this.getChildren(m.getId(),menus))) // 如果没有这一步那么，只会查一层; 可是查到的 children 可能还有children 所以需要这一步，当前查到的子菜单，可能还有子菜单，所以还需要查。
                // [这里的递归 并不会死循环，因为最后查不到 children 会返回 null]
                /**
                 *  .map(m->m.setChildren(this.getChildren(m,menus)))
                 *  因为 map 需要返回值，但是 我们 set 方法没有返回值，因此不能 直接用 map 方法 set。
                 *  需要 用 Lombok 把实体类编程 chain 链式编程后，才能使用，因为链式编程 set 方法会返回当前实体类
                 */
                .collect(Collectors.toList());
        return childrenList;
    }

    @Override
    public ResponseResult saveUpdate(MenuListDto menuListDto) {
        // 1. 统一响应
        ResponseResult responseResult;
        // 2. 父菜单 与当前菜单ID 是否一致

        if (menuListDto.getId().equals(menuListDto.getParentId())){ //2.1 true 【注意！！！ Long 不能使用 == 建议使用 equals】

            responseResult=ResponseResult.errorResult(500,"修改菜单'写博文'失败，上级菜单不能选择自己");

        }else{ // false

            SysMenu sysMenu = BeanCopyUtils.copyBean(menuListDto, SysMenu.class);

            boolean update = this.updateById(sysMenu);

            if (update){
                responseResult=ResponseResult.okResult();
            }else {
                responseResult=ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
            }
        }

        return responseResult;
    }

    @Override
    public ResponseResult<List<MenuTreeSelectVo>> getAllMenuByTree() {
        // 1. 获取所有菜单列表
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();

        wrapper.orderByAsc(SysMenu::getParentId);
        wrapper.orderByAsc(SysMenu::getOrderNum);

        List<SysMenu> list = this.list(wrapper);
        // 2. 转化成 VO
        List<MenuTreeSelectVo> list1 = list.stream()
                .map(c -> new MenuTreeSelectVo(null,c.getId(),c.getMenuName(),c.getParentId()))
                .collect(Collectors.toList());

        // 3. 只展示 parent_id = 0 的 也就是顶级菜单，因为其他的都是 以子菜单的形式展示。
        List<MenuTreeSelectVo> collect = list1.stream()
                .filter(l -> l.getParentId().equals(SystemConstants.FIRST_MENU_ID) ) // ==0L SystemConstants.FIRST_MENU_ID
                .collect(Collectors.toList());

        // 4. 填充子菜单
        List<MenuTreeSelectVo> collect2 = collect.stream()
                .map(c -> c.setChildren(this.getMenuChildren(c.getId(),list1)))
                .collect(Collectors.toList());


        // 4. 统一响应
        ResponseResult<List<MenuTreeSelectVo>> responseResult = ResponseResult.okResult(collect2);

        return responseResult;
    }


    /**
     *
     * @param id  当前菜单ID
     * @param list 菜单所有列表按照 ，parentID 和 orderNum 升序
     * @return List<MenuTreeSelectVo> 当前菜单ID的子菜单
     */
    public List<MenuTreeSelectVo> getMenuChildren(Long id,List<MenuTreeSelectVo> list){

        List<MenuTreeSelectVo> collect = list.stream()
                .filter(l -> l.getParentId().equals(id) )
                .map(l -> l.setChildren(this.getMenuChildren(l.getId(),list)))
                .collect(Collectors.toList());

        return collect;
    }

    @Override
    public ResponseResult<RoleMenuTreeByIdVo> getRoleMenuTreeByID(Long id) {
        // 1. 获取角色id 下所有 菜单列表
        List<Long> menuIdList = this.getBaseMapper().getMenuIdListByRoleId(id);

        // 2. 获取所有菜单权限列表
        ResponseResult<List<MenuTreeSelectVo>> allMenuByTree = this.getAllMenuByTree();
        List<MenuTreeSelectVo> data = allMenuByTree.getData();
        RoleMenuTreeByIdVo roleMenuTreeByIdVo = new RoleMenuTreeByIdVo(data,menuIdList);

        // 4.统一响应
        ResponseResult<RoleMenuTreeByIdVo> responseResult  =ResponseResult.okResult(roleMenuTreeByIdVo);

        return responseResult;
    }

}